#include "Game.h"
#include <pthread.h>
//#include "Client.h"

pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
Explosion Game::explosions[MAX_EXPLOSIONS];
GlobalSpriteResource* Game::gsr;
Enemy Game::enemies[MAX_ENEMIES];
SDL_Surface* Game::screen;
BulletFlight* Game::bf;
EnemyFlight* Game::ef;
Player* Game::player;
long int Game::score;
bool Game::KEYS[322];
Level* Game::level;
int Game::gs;

Game::Game() {

}

Game::~Game() {

}


int Game::init() {
    // initialize SDL video
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
        printf( "Unable to init SDL: %s\n", SDL_GetError() );
        return 1;
    }

    // make sure SDL cleans up before exit
    atexit(SDL_Quit);

    // create a new window
    screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE|SDL_DOUBLEBUF);
    if ( !screen ) {
        printf("Unable to set 640x480 video: %s\n", SDL_GetError());
        return 1;
    }

    SDL_EnableKeyRepeat(0,0);

    gsr = new GlobalSpriteResource();
    gsr->set(SR_SHIP_1, "sprites/spaceship.bmp", 1, 0);
    gsr->set(SR_BEAM_1, "sprites/beam_1.bmp", 2, 100);
    gsr->set(SR_BEAM_2, "sprites/beam_2.bmp", 1, 0);
    gsr->set(SR_BACKGROUND_1, "sprites/back_2.bmp", 1, 0);
    gsr->set(SR_BACKGROUND_2, "sprites/back_1.bmp", 1, 0);
    gsr->set(SR_ENEMY_1, "sprites/enemy_1.bmp",6,75);
    gsr->set(SR_EXPLOSION_1, "sprites/explosion_1.bmp",8,25);

    bf = new BulletFlight();
    ef = new EnemyFlight();

    score = 0;
    player = new Player();
    TTF_Init();
    font = TTF_OpenFont("digifaw.ttf", 32);

    reset();
//    SDL_TimerID bullet_timer = SDL_AddTimer(100, &handleBullets, void);
/*
    	Respuestas  *buffer = new Respuestas();
    	//Request object
    	our_data_request_t request;
    	//Function code that defines what we want
    	request.wFunctionCode = REQUEST_BASKET_MENU;
    	//We read the question
    	std::cout<<"Pregunta: "<< std::endl;
    	gets(request.un._question);

    	//Object to communicate with VMS Server
    	Client *obj;

    	obj =(Client*)malloc(sizeof(Client));
    	//Fill standard header
    	obj->FillOurHeaderMessage();
    	//Issue an open command to server
    	obj->client_startup();
    	//Send "request" to server and ask for a reply on given buffer up to given size in bytes
    	obj->read_from_server( buffer, sizeof(Respuestas), &request);

    	//Debug function to show read data from server
*/
    return 0;
}

void Game::run() {

	pthread_t threads[6];

	pthread_mutex_init(&count_mutex, NULL);
	pthread_cond_init (&count_threshold_cv, NULL);

	pthread_create(&threads[0],NULL,&Game::keyboard,NULL);
    //keyboard();
	pthread_create(&threads[1],NULL,&Game::handleInput,NULL);
    //handleInput();
    // clear screen
    SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));

    pthread_create(&threads[2],NULL,&Game::backGround,NULL);
    //backGround();

    pthread_create(&threads[3],NULL,&Game::handleEnemies,NULL);
    //handleEnemies();
    pthread_create(&threads[4],NULL,&Game::handleBullets,NULL);
    //handleBullets();
    pthread_create(&threads[5],NULL,&Game::handleExplosions,NULL);
    //handleExplosions();

    pthread_exit(NULL);
    handleLevel();
    handleScore();
    player->draw(screen, player->x, player->y);
    // finally, update the screen
    SDL_Flip(screen);

}

void Game::reset() {

    memset(KEYS,0,255);
    for(int i = 0; i < 256; i++) {
        KEYS[i] = false;
    }

    player->image = gsr->get(SR_SHIP_1);
    player->x = 320;
    player->y = 440;
    player->armor = 1;
    player->speed = 8;
    player->lastShot = SDL_GetTicks();
    player->shotDelay = 200;
    player->exists = true;
    level = new Level();
 //   level->loadLevel("levels/1.lvl");
    level->loadLevel(1);
    level->levelY = 0;
    level->levelScrollSpeed = 2;
    level->backgroundImage = SR_BACKGROUND_2;
    score = 0;
    level->levelStartTime = SDL_GetTicks();
}

void* Game::backGround(void*) {
    // draw background
    for(int y = -gsr->get(level->backgroundImage)->getWidth(); y <= screen->h; y += gsr->get(level->backgroundImage)->getHeight()) {
        for(int x = 0; x <= screen->w; x += gsr->get(level->backgroundImage)->getWidth()) {
            gsr->get(level->backgroundImage)->draw(screen, x, y+level->levelY);
        }
    }
    level->levelY += level->levelScrollSpeed;
    if(level->levelY > gsr->get(level->backgroundImage)->getHeight()) {
        level->levelY = 0;
    }
}

void* Game::handleInput(void*) {
    if(KEYS[SDLK_LEFT]) {
        if(player->x - player->speed >= 0) {
            player->x -= player->speed;
        }
    }
    if(KEYS[SDLK_RIGHT]) {
        if(player->x + player->speed <= screen->w) {
            player->x += player->speed;
        }
    }
    if(KEYS[SDLK_UP]) {
        if(player->y - player->speed >= 0) {
            player->y -= player->speed;
        }
    }
    if(KEYS[SDLK_DOWN]) {
        if(player->y + player->speed <= screen->h) {
            player->y += player->speed;
        }
    }
    if(KEYS[SDLK_s]) {
        if(SDL_GetTicks() - player->lastShot > player->shotDelay) {
           if(player->exists){
        	   shootbeam(player->beam);
           }
        }
    }
    if(KEYS[SDLK_q]) {
        if(player->beam == PLAYER_BEAM_CHARGE) {
            player->beam = PLAYER_BEAM_NORMAL;
        } else {
            player->beam = PLAYER_BEAM_CHARGE;
        }
    }
    if(KEYS[SDLK_r]) {
    	pthread_mutex_lock(&count_mutex);
        reset();
        pthread_mutex_unlock(&count_mutex);
    }

    if(KEYS[SDLK_ESCAPE]) {
        gs = GS_DONE;
    }
}

void* Game::keyboard(void*) {
        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            // check for messages
            switch (event.type) {
                // exit if the window is closed
            case SDL_QUIT:
                gs = GS_DONE;
                break;
                // check for keypresses
            case SDL_KEYDOWN:
                KEYS[event.key.keysym.sym] = true;
                break;
            case SDL_KEYUP:
                KEYS[event.key.keysym.sym] = false;
                break;
            default:
                break;
			}
        } // end of message processing
}


void* Game::handleBullets(void*) {
    for(int i = 0; i < MAX_BULLETS; i++) {
        if(player->bullets[i].exists) {

            bf->process(player->bullets[i]);

            for(int j = 0; j < MAX_ENEMIES; j++) {
                if(enemies[j].exists == true ) {
                    if(enemies[j].image->rectCollide(enemies[j].x, enemies[j].y, *player->bullets[i].image, player->bullets[i].x, player->bullets[i].y)) {
                        if(enemies[j].image->pixelCollide(enemies[j].x, enemies[j].y, *player->bullets[i].image, player->bullets[i].x, player->bullets[i].y)) {
                            enemies[j].armor -= player->bullets[i].damage;
                            player->bullets[i].exists = false;
                        }
                    }
                }
            }

            player->bullets[i].draw(screen, player->bullets[i].x, player->bullets[i].y);
            if(player->bullets[i].y < -50 || player->bullets[i].y > screen->h +50 ||
                    player->bullets[i].x < -50 || player->bullets[i].x > screen->w +50) {
                player->bullets[i].exists = false;
            }
        }
    }
}


void* Game::shootbeam(int beam) {

    if(beam == PLAYER_BEAM_NORMAL) {
        player->lastShot = SDL_GetTicks();

        int center = player->x + player->image->getWidth()/2 - gsr->get(SR_BEAM_1)->getWidth()/2;
        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 0, -20, 1, BULLET_FLIGHT_NORMAL);
        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, 0, -20, 1, BULLET_FLIGHT_NORMAL);
        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 4, -sqrt(384), 1, BULLET_FLIGHT_NORMAL);
        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -4, -sqrt(384), 1, BULLET_FLIGHT_NORMAL);

    } else if(beam == PLAYER_BEAM_CHARGE) {
    	player->lastShot = SDL_GetTicks();

    	        int center = player->x + player->image->getWidth()/2 - gsr->get(SR_BEAM_1)->getWidth()/2;
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 0, -20, 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, 0, -20, 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 4, -sqrt(384), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -4, -sqrt(384), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 8, -sqrt(336), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -8, -sqrt(336), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 12, -sqrt(266), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -12, -sqrt(266), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 16, -sqrt(204), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -16, -sqrt(204), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 10, 18, -sqrt(76), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 10, -18, -sqrt(76), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y - 3, 20, 0, 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y - 3, -20, 0, 1, BULLET_FLIGHT_NORMAL);

    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 0, 20, 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, 0, 20, 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 4, sqrt(384), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, -4, sqrt(384), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 8, sqrt(336), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, -8, sqrt(336), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 12, sqrt(266), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, -12, sqrt(266), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 16, sqrt(204), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, -16, sqrt(204), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center + 13, player->y + 5, 18, sqrt(76), 1, BULLET_FLIGHT_NORMAL);
    	        player->addBullet(gsr->get(SR_BEAM_1), center - 13, player->y + 5, -18, sqrt(76), 1, BULLET_FLIGHT_NORMAL);
    }

}

void Game::handleLevel() {
    while(level->isNextEvent()) {
        Command* c = level->lvl_cmds.at(0);
        cout << "Next Event" << endl;
        if(c->cmd == 'E') {

            int i = getEmptyEnemy();
            if(i != -1) {
                cout << "Next Event - Add Enemy" << endl;
                enemies[i].exists = true;
                enemies[i].image = gsr->get(c->p1);
                enemies[i].x = c->p2;
                enemies[i].y = c->p3;
                enemies[i].shotDelay = c->p4;
                enemies[i].damage = c->p5;
                enemies[i].armor = c->p6;
                enemies[i].beam = c->p7;
                enemies[i].flightType = c->p8;
                enemies[i].speedX = c->p9;
                enemies[i].speedY = c->p10;
            }
        }


        level->lvl_cmds.erase(level->lvl_cmds.begin() + 0);
    }

}

void* Game::handleEnemies(void*) {
    for(int i = 0; i < MAX_ENEMIES; i++) {
        if(enemies[i].exists) {
            enemies[i].x += enemies[i].speedX;
            enemies[i].y += enemies[i].speedY;
            enemies[i].draw(screen, enemies[i].x, enemies[i].y);
            if(enemies[i].y < -100 || enemies[i].y > screen->h + 100 ||
                enemies[i].x < -100 || enemies[i].x > screen->w + 100) {
                enemies[i].exists = false;
            }
            if(enemies[i].armor < 0) {
                enemies[i].exists = false;
                score += 1000;
                addExplosion(SR_EXPLOSION_1, enemies[i].x,  enemies[i].y);
            }

            if(enemies[i].image->rectCollide(enemies[i].x, enemies[i].y, *player->image, player->x, player->y)) {
                if(enemies[i].image->pixelCollide(enemies[i].x, enemies[i].y, *player->image, player->x, player->y)) {
                    if(player->exists){
                    	enemies[i].armor -= player->bullets[i].damage;
                    	addExplosion(SR_EXPLOSION_1, player->x, player->y);
                    	player->exists = false;
                    }
                }
            }
        }
    }
}


int Game::getEmptyEnemy() {
    for(int i = 0; i < MAX_ENEMIES; i++) {
        if(!enemies[i].exists) {
            return i;
        }
    }
    return -1;
}


void* Game::handleExplosions(void*) {
    for(int i = 0; i < MAX_EXPLOSIONS; i++) {
        if(explosions[i].exists) {
            if(SDL_GetTicks() - explosions[i].startTime >=  explosions[i].duration) {
                explosions[i].exists = false;
            } else {
                explosions[i].draw(screen, explosions[i].x, explosions[i].y);
            }
        }
    }
}


int Game::getEmptyExplosion() {
    for(int i = 0; i < MAX_EXPLOSIONS; i++) {
        if(!explosions[i].exists) {
            return i;
        }
    }
    return -1;
}

void* Game::addExplosion(int image, int x, int y) {
    int i = getEmptyExplosion();
    if(i > -1) {
        explosions[i].exists = true;
        explosions[i].image = gsr->get(image);
        explosions[i].startTime = SDL_GetTicks();
        explosions[i].duration = explosions[i].image->speed * explosions[i].image-> maxFrames;
        explosions[i].x = x;
        explosions[i].y = y;
    }
}

void Game::handleScore(){
	SDL_Surface* txtSurface;
	SDL_Color colour = {255,255,255,0};
	char scoreDest[50];

		if(!(txtSurface = TTF_RenderText_Solid(font, itoa(score,scoreDest,10), colour))){
			printf("TTF_Print: %s\n", TTF_GetError());
			if(txtSurface != NULL)
				SDL_FreeSurface(txtSurface);
			return;
		}
		SDL_Rect dest;
		dest.x = 500 - (int)(txtSurface->w/2);
		dest.y = 60 - (int)(txtSurface->h/2);
		SDL_BlitSurface(txtSurface, NULL,screen , &dest);
		SDL_FreeSurface(txtSurface);
	return;
}
